﻿using System;
using System.Collections.ObjectModel;
using System.Web.Mvc;
using System.Web.Mvc.Html;

/*
<% using (Html.BeginForm().AddClientValidationRules(ViewContext.FormContext, new ValidationRuleDescriptorCollection()
   {
       new ValidationRuleDescriptor("price", true, new ModelClientValidationRuleCollection() {
           new ModelClientValidationRequiredRule("It is a custom rule."),
           new ModelClientValidationRangeRule("Value must be greater than zero, and less than or equal to 10.", 0, 10)
       })
   }))
   { %>

// ...............
<% } %>
*/

namespace Webbness.Mvc.Extensions
{
    /// <summary>
    /// Represents support for adding client validation rules.
    /// </summary>
    public static class MvcFormExtensions
    {
        /// <summary>
        /// Adds the client validation rules to specified fields.
        /// </summary>
        /// <param name="form">The MVC form.</param>
        /// <param name="formContext">Context of the form.</param>
        /// <param name="validationRuleDescriptors">Descriptors of fields and related validation rules.</param>
        /// <returns>An <see cref="MvcForm"/> (for fluent syntax).</returns>
        /// <exception cref="System.ArgumentNullException"><paramref name="form"/>, <paramref name="formContext"/> or <paramref name="validationRuleDescriptors"/> is null.</exception>
        public static MvcForm AddClientValidationRules(this MvcForm form, FormContext formContext, ValidationRuleDescriptorCollection validationRuleDescriptors)
        {
            foreach (ValidationRuleDescriptor descriptor in validationRuleDescriptors)
            {
                if (descriptor.Rules == null || descriptor.Rules.Count == 0) continue;

                FieldValidationMetadata metadata = formContext.GetValidationMetadataForField(descriptor.FieldName, descriptor.CreateIfNotFound);

                foreach (ModelClientValidationRule rule in descriptor.Rules)
                    metadata.ValidationRules.Add(rule);
            }

            return form;
        }
    }

    /// <summary>
    /// Encapsulates the name of the field and related client validation rules.
    /// </summary>
    public class ValidationRuleDescriptor
    {
        #region Fields

        private string _fieldName;
        private ModelClientValidationRuleCollection _rules;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="ValidationRuleDescriptor"/> class.
        /// </summary>
        public ValidationRuleDescriptor()
        {
            CreateIfNotFound = true;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ValidationRuleDescriptor"/> class.
        /// </summary>
        /// <param name="fieldName">Name of the associated field.</param>
        public ValidationRuleDescriptor(string fieldName)
            : this()
        {
            FieldName = fieldName;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ValidationRuleDescriptor"/> class.
        /// </summary>
        /// <param name="fieldName">Name of the associated field.</param>
        /// <param name="createIfNotFound">true to create a validation value if one is not found; otherwise, false.</param>
        public ValidationRuleDescriptor(string fieldName, bool createIfNotFound)
            : this(fieldName)
        {
            CreateIfNotFound = createIfNotFound;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ValidationRuleDescriptor"/> class.
        /// </summary>
        /// <param name="fieldName">Name of the associated field.</param>
        /// <param name="rules">Client validation rules.</param>
        public ValidationRuleDescriptor(string fieldName, ModelClientValidationRuleCollection rules)
            : this(fieldName)
        {
            Rules = rules;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ValidationRuleDescriptor"/> class.
        /// </summary>
        /// <param name="fieldName">Name of the associated field.</param>
        /// <param name="createIfNotFound">true to create a validation value if one is not found; otherwise, false.</param>
        /// <param name="rules">Client validation rules.</param>
        public ValidationRuleDescriptor(string fieldName, bool createIfNotFound, ModelClientValidationRuleCollection rules)
            : this(fieldName, createIfNotFound)
        {
            Rules = rules;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets a name of the field.
        /// </summary>
        public string FieldName
        {
            get { return _fieldName; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("FieldName");
                if (string.IsNullOrEmpty(value))
                    throw new ArgumentException("Cannot be an empty string.", "FieldName");

                _fieldName = value;
            }
        }

        /// <summary>
        /// Gets or sets a value that indicates what to do if the validation value is not found.
        /// </summary>
        public bool CreateIfNotFound { get; set; }

        /// <summary>
        /// Gets or sets a collection of client validation rules.
        /// </summary>
        public ModelClientValidationRuleCollection Rules
        {
            get { return _rules; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("Rules");

                _rules = value;
            }
        }

        #endregion
    }

    /// <summary>
    /// A collection of <see cref="ValidationRuleDescriptor"/> instances representing fields and related validation rules.
    /// </summary>
    [Serializable]
    public class ValidationRuleDescriptorCollection : Collection<ValidationRuleDescriptor>
    {
    }

    /// <summary>
    /// A collection of <see cref="ModelClientValidationRule"/> instances representing set of client validation rules.
    /// </summary>
    [Serializable]
    public class ModelClientValidationRuleCollection : Collection<ModelClientValidationRule>
    {
    }
}